/**********************************************************************
* Filename: Voo.java
* Created: 2013/05/30
* @author Fernando Brito e Abreu
**********************************************************************/
package org.quasar.airNova.businessLayer;

import org.quasar.airNova.persistenceLayer.Database;

import java.util.Set;
import java.util.HashSet;

public class Voo
{
	
	/***********************************************************
	* @return all instances of class Voo
	***********************************************************/
	public static Set<Voo> allInstances()
	{
		return Database.allInstances(Voo.class);
	}
	
	private Aeroporto chegada;
	private Aeroporto partida;
	private Instant hora_chegada;
	private Instant hora_partida;
	private int milhas;
	private String numero;
	
	/**********************************************************************
	* Default constructor
	**********************************************************************/
	public Voo()
	{
	}
	
	/**********************************************************************
	* Parameterized constructor
	* @param chegada the chegada to initialize
	* @param partida the partida to initialize
	* @param hora_chegada the hora_chegada to initialize
	* @param hora_partida the hora_partida to initialize
	* @param milhas the milhas to initialize
	* @param numero the numero to initialize
	**********************************************************************/
	public Voo(Aeroporto chegada, Aeroporto partida, Instant hora_chegada, Instant hora_partida, int milhas, String numero)
	{
		this.chegada = chegada;
		this.partida = partida;
		this.hora_chegada = hora_chegada;
		this.hora_partida = hora_partida;
		this.milhas = milhas;
		this.numero = numero;
	}
	
	/**********************************************************************
	* ONE2MANY getter for Voo[*] <-> Aeroporto[1]
	* @return the chegada of the voo
	**********************************************************************/
	public Aeroporto chegada()
	{
		return chegada;
	}
	
	/**********************************************************************
	* ONE2MANY setter for Voo[*] <-> Aeroporto[1]
	* @param chegada the chegada to set
	**********************************************************************/
	public void setChegada(Aeroporto chegada)
	{
		this.chegada = chegada;
	}
	
	/**********************************************************************
	* ONE2MANY getter for Voo[*] <-> Aeroporto[1]
	* @return the partida of the voo
	**********************************************************************/
	public Aeroporto partida()
	{
		return partida;
	}
	
	/**********************************************************************
	* ONE2MANY setter for Voo[*] <-> Aeroporto[1]
	* @param partida the partida to set
	**********************************************************************/
	public void setPartida(Aeroporto partida)
	{
		this.partida = partida;
	}
	
	/**********************************************************************
	* Standard attribute getter
	* @return the hora_chegada of the voo
	**********************************************************************/
	public Instant hora_chegada()
	{
		return hora_chegada;
	}
	
	/**********************************************************************
	* Standard attribute setter
	* @param hora_chegada the hora_chegada to set
	**********************************************************************/
	public void setHora_chegada(Instant hora_chegada)
	{
		this.hora_chegada = hora_chegada;
	}
	
	/**********************************************************************
	* Standard attribute getter
	* @return the hora_partida of the voo
	**********************************************************************/
	public Instant hora_partida()
	{
		return hora_partida;
	}
	
	/**********************************************************************
	* Standard attribute setter
	* @param hora_partida the hora_partida to set
	**********************************************************************/
	public void setHora_partida(Instant hora_partida)
	{
		this.hora_partida = hora_partida;
	}
	
	/**********************************************************************
	* Standard attribute getter
	* @return the milhas of the voo
	**********************************************************************/
	public int milhas()
	{
		return milhas;
	}
	
	/**********************************************************************
	* Standard attribute setter
	* @param milhas the milhas to set
	**********************************************************************/
	public void setMilhas(int milhas)
	{
		this.milhas = milhas;
	}
	
	/**********************************************************************
	* Standard attribute getter
	* @return the numero of the voo
	**********************************************************************/
	public String numero()
	{
		return numero;
	}
	
	/**********************************************************************
	* Standard attribute setter
	* @param numero the numero to set
	**********************************************************************/
	public void setNumero(String numero)
	{
		this.numero = numero;
	}
	
	/**********************************************************************
	* MEMBER2ASSOCIATIVE getter for Voo[*] <-> Realizacao[*]
	* @return the realizacao of the realiza_se
	**********************************************************************/
	public Set<Realizacao> realizacao()
	{
		Set<Realizacao> result = new HashSet<Realizacao>();
		for (Realizacao x : Realizacao.allInstances())
			if (x.realiza_se()  ==  this)
				result.add(x);
		return result;
	}
	
	/**********************************************************************
	* MEMBER2ASSOCIATIVE setter for Voo[*] <-> Realizacao[*]
	* @param realizacao the realizacao to set
	**********************************************************************/
	public void setRealizacao(Set<Realizacao> realizacao)
	{
		for (Realizacao x : realizacao)
			x.setRealiza_se(this);
	}
	
	/**********************************************************************
	* MEMBER2MEMBER getter for Voo[*] <-> CalendarDate[*]
	* @return the ocorre_em of the realiza_se
	**********************************************************************/
	public Set<CalendarDate> ocorre_em()
	{
		Set<CalendarDate> result = new HashSet<CalendarDate>();
		for (Realizacao x : Realizacao.allInstances())
			if (x.realiza_se()  ==  this)
				result.add(x.ocorre_em());
		return result;
	}
	
	/**********************************************************************
	* MEMBER2MEMBER setter for Voo[*] <-> CalendarDate[*]
	* @param ocorre_em the ocorre_em to set
	**********************************************************************/
	public void setOcorre_em(Set<CalendarDate> ocorre_em)
	{
		for (CalendarDate t : ocorre_em)
			for (Realizacao x : Realizacao.allInstances())
				if (x.realiza_se() == this)
					x.setOcorre_em(t);
	}
	
	/**********************************************************************
	* User-defined operation specified in SOIL/OCL
	* @param cd the cd to set
	**********************************************************************/
	public void CancelarVoo(CalendarDate cd)
	{
		if (verificaData()== true){
			Realizacao rea = null;
			for (Realizacao each : this.realizacao()) {
				if(each.ocorre_em().isEqual(cd)){
					rea=each;
					rea.setCancelada(true);
					break;
					
				}
			}
			Database.update(this);
			Database.close();
		}
	}
	
			
		
		private boolean verificaData() {
			{
				for (CalendarDate calander : this.ocorre_em() ) 
					if (calander.isAfter(calander.now())){
						return true;
					}
				}
			 return false;
	}
	
	//	PRE-CONDITIONS (TODO)
	/*
	pre DataDeCancelamentoAnteriorAoDiaDoVoo
		self.ocorre_em->select(c : CalendarDate | (c = cd))->asSequence->first.isAfter(self.ocorre_em->select(c : CalendarDate | (c = cd))->asSequence->first.today())
	
	pre EstadoCanceladaDefinido
		self.ocorre_em->select(c : CalendarDate | (c = cd))->collectNested($e : CalendarDate | $e.realizacao)->flatten->asSequence->first.cancelada.isDefined
	
	*/
	
	//	POST-CONDITIONS (TODO)
	/*
	post VooCancelado
		(self.ocorre_em->select(c : CalendarDate | (c = cd))->collectNested($e : CalendarDate | $e.realizacao)->flatten->asSequence->first.cancelada = true)
	
	*/
	
	/**********************************************************************
	* User-defined operation specified in SOIL/OCL
	* @param inicio the inicio to set
	* @param fim the fim to set
	**********************************************************************/
	public double OcupacaoMedia(CalendarDate inicio, CalendarDate fim)
	{
		//	TODO
		//	return let realizacoes_voo : Set(Realizacao) = self.realizacao->reject($elem0 : Realizacao | $elem0.cancelada)->select($elem1 : Realizacao | ((($elem1.ocorre_em = inicio) or ($elem1.ocorre_em = fim)) or ($elem1.ocorre_em.isAfter(inicio) and $elem1.ocorre_em.isBefore(fim)))) in (realizacoes_voo->collectNested($e : Realizacao | $e.bilhetes)->flatten->size / realizacoes_voo->collectNested($e : Realizacao | $e.aviao)->collectNested($e : Aviao | $e.Lotacao())->sum)
		return -1;
	}
	
	/**********************************************************************
	* Object serializer
	**********************************************************************/
	public String toString()
	{
		return "Voo [chegada=" + chegada + ", partida=" + partida + ", hora_chegada=" + hora_chegada + ", hora_partida=" + hora_partida + ", milhas=" + milhas + ", numero=" + numero + "]";
	}
	
	//	-------------------------------------------------------------------------------
	//	INVARIANTS (TODO)
	/*
	inv vooTemDePossuirMilhasSuperiorAZero
		(self.milhas > 0)
	
	inv realizarVoo
		(self.realizacao->collect($elem4 : Realizacao | $elem4.bilhetes)->size > 1)
	
	inv oNumeroDoVooEUnico
		Voo.allInstances->excluding(self)->iterate(v : Voo; dup : Boolean = true | ((v <> self) and (v.numero <> self.numero)))
	
	inv horaPartidaSuperiorHoraChegada
		self.hora_partida.isBefore(self.hora_chegada)
	
	*/
}
